import {oRequest_CookieEngine} from "./request_cookie";
import {oRequest_LocalStorageEngine} from "./request_locatstorage";
import {oRequest_APIEngine} from "./request_api";
import {oSecurityEngine} from "./security";
import {round} from "echarts/lib/util/number";

function oRequestEngine(engine) {

  this.engine = engine;

  this.ajaxSequence = 0; // 防止ajax错乱，因为异步返回有延迟，可能先发起的后面才回来，所以需要加一个下标索引，针对特殊请求，可判断先后顺序。
  this.ajaxSequenceLoad = 0; // 处理完的。
  this.ajaxQueryList = [];
  this.init();
}

oRequestEngine.prototype = {
  // 继承插件初始化函数
  init: function () {
    let o = this;

    // 初始化cookies组件
    this.cookie = new oRequest_CookieEngine(this.engine);
    // 初始化localstorage，后续如果函数变多，可随时扩展为插件。
    this.localstorage = new oRequest_LocalStorageEngine(this.engine);
    // 初始化安全模块
    this.security = new oSecurityEngine(this.engine);
    // 初始化接口模块（自带令牌认证）
    this.api = new oRequest_APIEngine(this.engine);
    // 生成加载遮罩
    this.mask = $(document.createElement("div"));

    this.loadProcess = 0;

    if (this.engine.tools.isMobile()) {
      this.mask.addClass("maskApp");
    } else {
      this.mask.addClass("mask");
    }
    //////////////////////////////////////// 获取配置数据 ///////////////////////////////////////////
    // 以下是异步的
    // $.getScript("/static/conf.js", function () {
    //   // 加载模式
    //   o.engine.config = conf;
    //   conf = null;
    // });
    this.getScript("/static/conf.js", false, function () {
      // data = conf;
      o.engine.config = conf;
      conf = null;
    });

    //////////////////////////// 从Localstorage中获取Passport的数据//////////////////////////////////
    let user_passport = this.localstorage.get("user_passport");
    // 如果有护照数据，则填充至框架。
    if (!this.engine.tools.IsNullOrEmpty(user_passport)) {
      this.engine.passport = user_passport;
    }
  },
  getScript: function (url, async, callback) {
    this.getAjax("GET", "script", url, async, null, callback);
  },
  getJSON: function (url, callback) {
    this.getAjax("GET", "json", url, false, null, callback);
  },
  getJSONP: function (url, data, callback) {
    this.getAjax("GET", "jsonp", url, false, data, callback);
  },
  getHTML: function (url, success, error) {
    // 尝试使用vue-resource插件实现资源的加载
    this.engine.vue.$http.get(url)
      .then((response) => {
        if (success) {
          success(response.data);
        }
      })
      .catch(function (response) {
        if (error) {
          error(response);
        }
      });
    // 读取本地资源时，以下方式不兼容vue，因为被VUE的路由档掉了，外部地址是正常的。
    // this.getAjax("GET", "html", url, async, null, success, error);
  },
  // 这个方法是getAjax的后台轮询监听方法。
  pollingAjax: function () {
    let sDate = new Date();  //开始时间
    let o = this;
    let processQuery = null;

    // 这里因为涉及到读配置，在初期读配置的时候还没有加载tips，所以这里会报错。
    // 只有config读取文件配置时是没有的，/static/conf.js 这个没有，后面都有了。
    if (this.engine.tips) {
      this.engine.tips.debug("[" + this.engine.tools.date("yyyy/MM/dd HH:mm:ss") + "] 请求引擎队列长度：" + this.ajaxQueryList.length);
    }

    $(this.ajaxQueryList).each(function (index, item) {
      // 找到需要处理的Ajax请求
      if (item.status == 0) {
        item.status = 777;// 标记为已处理,7是我的幸运数字，哈哈哈。
        processQuery = item; // 赋值
        return false; // 找到一个则跳出，执行完Ajax回调触发下一轮询。
      }
    });

    // 判断是否找到Ajax任务
    if (processQuery != null) {
      // 执行Ajax请求，成功后调用setTimeout
      $.ajax({
        type: processQuery.type,
        dataType: processQuery.dataType,// script/json/xml/html/text/jsonp
        cache: false,
        async: processQuery.async,// true：异步；false：同步；因为有大量并发，所以必须改成同步，否则会导致数据错乱。及其严重。
        url: processQuery.url,
        contentType: 'application/json',//charset=utf-8
        // 这里做判断主要是百度接口采用jsonp，而且是不能转义的。。目前偷懒，如果后续还有特殊类型，是否JSON转移需要设置一个属性的。
        data: (processQuery.dataType == "jsonp" ? processQuery.data : JSON.stringify(processQuery.data))
      }).done(function (rdata) {

        // 只有config读取文件配置时是没有的，/static/conf.js 这个没有，后面都有了。
        if (o.engine.tips) {
          o.engine.tips.debug("[" + o.engine.tools.date("yyyy/MM/dd HH:mm:ss") + "] 请求地址：" + processQuery.url
            + ", 请求数据：");
          console.log(processQuery.data);
          console.log("请求地址返回：");
          console.log(rdata);
        }
        // 记录总时间、执行顺序
        if (rdata.status && rdata.status.totalTimer) {
          // 记录Ajax完整阶段的执行时间。
          rdata.status.totalTimer = executeTime();
          rdata.status.sequenceIndex = processQuery.sequence;
        }
        // 触发回调
        if (processQuery.callback) {
          processQuery.callback(rdata);
        }

        // 执行成功，将当前项删除，curr：循环项，i：索引。
        o.ajaxQueryList = $.grep(o.ajaxQueryList, function (curr, i) {
          return curr != processQuery;
        });

        // 执行成功以后，瞬时启动下一个循环。
        o.pollingAjax();

        // 有一个bug，当断网的时候，这个队列会越来越多？。。


        ///////////////////////////////////////////////////

      }).fail(function (msg) {
        // 能走到这里的一定是八爪鱼API无法处理的，一般情况下这是不可能的，因为八爪鱼API全部会通过Status Code来表达异常，而不会报错。
        if (processQuery.callback) {
          let edata = {
            content: "",
            status: {code: 500, message: msg.responseText || "未请求到资源，请确保资源的有效性。"},
            totalTimer: executeTime()
          };
          processQuery.callback(edata);
        }

        //执行失败以后，延时2秒再次请求。
        setTimeout(function () {
          // 重置为未获取状态。
          processQuery.status = 0;
          o.pollingAjax();
        }, 2000);

      });
    } else {
      // 如果请求队列还有数据，则继续执行循环，如果队列清空，则监听停止，防止浏览器死机。
      if (this.ajaxQueryList.length > 0) {
        setTimeout(function () {
          o.pollingAjax();
        }, 1);
      } else {
        // 确认没有请求数据了，隐藏遮罩。
        // this.hiddenMask();
      }
    }

    // 获取计算时间
    function executeTime() {
      let diff_date = new Date() - sDate.getTime();  //时间差的毫秒数
      let diff_second = diff_date % (24 * 3600 * 1000);    //计算天数后剩余的毫秒数
      // return (diff_second / 1000) + "s";
      return (diff_second / 1000);
    }
  },
  // 发起Ajax请求，其实这边是统一接口，然后存入Ajax队列，pollingAjax方法会调度执行里面的代码
  getAjax_old: function (type, dataType, url, async, data, callback) {
    // 记录流水号
    this.ajaxSequence = this.ajaxSequence + 1;
    // 将Ajax请求追加至Ajax Query List，如果请求列表已经为0，则重新启动触发器。
    this.ajaxQueryList.push({status: 0, sequence: this.ajaxSequence, type, dataType, url, async, data, callback});
    // 如果只有当前1条任务，则说明之前监听已经结束，现在重新启动。
    if (this.ajaxQueryList.length == 1) {
      // 新启动任务时触发遮罩
      // this.showMask();
      // 启动任务
      this.pollingAjax();
    }
  },
  // 此处启用promise时 需要把conf的获取设置为优先强制。
  getAjax_promise: function (type, dataType, url, async, data, callback) {
    let sDate = new Date();  //开始时间

    // 异步处理
    let promise = new Promise(function (resolve, reject) {
      // 执行Ajax请求，成功后调用setTimeout
      $.ajax({
        type: type,
        dataType: dataType,// script/json/xml/html/text/jsonp
        cache: false,
        async: async,// true：异步；false：同步；因为有大量并发，所以必须改成同步，否则会导致数据错乱。及其严重。
        url: url,
        contentType: 'application/json',//charset=utf-8
        // 这里做判断主要是百度接口采用jsonp，而且是不能转义的。。目前偷懒，如果后续还有特殊类型，是否JSON转移需要设置一个属性的。
        data: (dataType == "jsonp" ? data : JSON.stringify(data))
      }).done(function (rdata) {
        // 记录总时间、执行顺序
        if (rdata.status && rdata.status.totalTimer) {
          // 记录Ajax完整阶段的执行时间。
          rdata.status.totalTimer = executeTime();
          rdata.status.sequenceIndex = sequence;
        }
        resolve(rdata);
      }).fail(function (msg) {
        // 能走到这里的一定是八爪鱼API无法处理的，一般情况下这是不可能的，因为八爪鱼API全部会通过Status Code来表达异常，而不会报错。
        let edata = {
          content: "",
          status: {code: 500, message: msg.responseText || "未请求到资源，请确保资源的有效性。", totalTimer: executeTime()},
        };
        reject(edata);
      });
    });

    promise.then(function (successMessage) {
      // 触发回调
      if (callback) {
        callback(successMessage);
      }
    }).catch(function (errorMessage) {
      // 触发回调
      if (callback) {
        callback(errorMessage);
      }
    });

    // 获取计算时间
    function executeTime() {
      let diff_date = new Date() - sDate.getTime();  //时间差的毫秒数
      let diff_second = diff_date % (24 * 3600 * 1000);    //计算天数后剩余的毫秒数
      // return (diff_second / 1000) + "s";
      return (diff_second / 1000);
    }
  },
  // 加上遮罩。/////（））（）（@）*（*（#*（！@*#（！@#
  // 12321321321312……#&！@（#&*（！#
  getAjax: function (type, dataType, url, async, data, callback) {
    // 记录流水号
    this.ajaxSequence = this.ajaxSequence + 1;
    if (this.ajaxSequence == 1) {
      // 新启动任务时触发遮罩
      this.showMask();
    }
    let o = this;
    let sDate = new Date();  //开始时间
    // let sequence = 1;

    // 执行Ajax请求，成功后调用setTimeout
    $.ajax({
      type: type,
      dataType: dataType,// script/json/xml/html/text/jsonp
      cache: false,
      async: async,// true：异步；false：同步；因为有大量并发，所以必须改成同步，否则会导致数据错乱。及其严重。
      url: url,
      contentType: 'application/json',//charset=utf-8
      // 这里做判断主要是百度接口采用jsonp，而且是不能转义的。。目前偷懒，如果后续还有特殊类型，是否JSON转移需要设置一个属性的。
      data: (dataType == "jsonp" ? data : JSON.stringify(data))
    }).done(function (rdata) {
      parseAjaxSequence();
      // 记录总时间、执行顺序
      if (rdata.status != null && rdata.status.totalTimer != null) {
        // 记录Ajax完整阶段的执行时间。
        rdata.status.totalTimer = executeTime();
        // console.error(rdata.status.totalTimer);
        // rdata.status.sequenceIndex = sequence;
      }
      // 触发回调
      if (callback) {
        callback(rdata);
      }
    }).fail(function (msg) {
      parseAjaxSequence();
      // 能走到这里的一定是八爪鱼API无法处理的，一般情况下这是不可能的，因为八爪鱼API全部会通过Status Code来表达异常，而不会报错。
      if (callback) {
        let edata = {
          content: "",
          status: {code: 500, message: msg || "未请求到资源，请确保资源的有效性。", totalTimer: 0.00},
        };
        callback(edata);
      }
    });

    // 获取计算时间
    function executeTime() {
      let diff_date = new Date() - sDate.getTime();  //时间差的毫秒数
      let diff_second = diff_date % (24 * 3600 * 1000);    //计算天数后剩余的毫秒数
      // console.error(diff_date + "|" + diff_second + "|" + (diff_second / 1000));
      // return (diff_second / 1000) + "s";
      return (diff_second / 1000);
    }

    function parseAjaxSequence() {
      o.ajaxSequence = o.ajaxSequence - 1;
    }
  },
  getUpload: function (url, data, processCallback, completedCallback) {
    let o = this;
    let pieceSize = 100;
    let chunkSize = pieceSize * 1024; // 100 KB 一片
    let chunkCount = Math.ceil(data.size / chunkSize); // 总片数
    // 打包文件信息
    let info = {
      name: data.name,
      guid: this.engine.tools.guid(),
      chunkIndex: -1, // 当前分片索引
      chunkCount: chunkCount, // 总分片数
      chunkSize: chunkSize,   // 每个分片大小
      size: data.size,         // 文件总大小
      // moduleName: data.moduleName
    };

    // 执行分片上传
    chunkUpload(0);

    function chunkUpload(chunkIndex) {
      // console.log(chunkIndex);
      // 定义索引
      info.chunkIndex = chunkIndex;
      // 获得指定的分块数据
      let chunkData = o.getUploadToChunk(data, info.chunkIndex, chunkSize);

      // 重新构建form表单伤处啊你数据
      let fetchForm = new FormData();
      fetchForm.append("info", JSON.stringify(info));   // 分片上传数据信息，因为form表单只能传递字符串。
      fetchForm.append("file", chunkData.chunk);        // 当前分片数据

      // console.log("chunkCount: " + chunkCount);
      // console.log(chunkData);
      // console.log(data);
      // console.log(fetchForm);
      // // 执行上传
      // let formData = new FormData();
      // formData.append("file", data);// 此处file名称要和后台API保持一致。

      $.ajax({
        url: url, /*接口域名地址*/
        type: 'post',
        data: fetchForm,
        contentType: false,// 告诉jQuery不要去设置Content-Type请求头
        processData: false,// 告诉jQuery不要去处理发送的数据
        success: function (returnData) {
          let percentage = ((chunkIndex + 1) / chunkCount) * 100;
          // 进度回调
          if (processCallback) {
            // 标记上传完成
            processCallback({uid: data.uid, name: data.name, percentage: percentage});
          }

          // 如果上传到最后一个包，并且成功的，则执行回调。
          if (chunkIndex == (chunkCount - 1)) {
            if (completedCallback) {
              completedCallback(returnData);
            }
          }
          // 继续执行分包上传
          else {
            chunkUpload(chunkIndex + 1);
          }
        }
      });
    }
  },
  // 上传文件分块函数
  getUploadToChunk: function (file, currentChunk, chunkSize) {
    console.log("file",file)
    let start = currentChunk * chunkSize;
    let end = Math.min(file.size, start + chunkSize);
    let chunk = file.slice(start, end);
    return {start, end, chunk};
  },
  // 导出下载文件
  getDownload: function (url, file, callback) {
    // console.log(JSON.stringify(file));

    // 创建Form表单
    let form = $('<form></form>');
    // 确保不走缓存。
    form.attr('action', url + "&r=" + Math.random());
    form.attr('method', 'post');

    let input = $('<input type="text" />');
    input.attr('value', JSON.stringify(file));
    input.attr('name', "FILEDATA");
    form.append(input);
    // 提交➕删除
    form.appendTo('body').submit().remove();

    // 如果有回调，则触发。
    if (callback) {
      callback({
        content: file,
        status: {
          code: 200,
          message: "Success."
        }
      });
    }

  },
  // 导出Excel文件
  getExport: function (url, source, callback) {
    // 修正文件名
    let name = source.exportName || "[" + this.engine.tools.date("yyyy-MM-dd") + "]数据文件";

    // 创建对象
    let param = {
      FILENAME: name,
      FILEDATA: source.data,
    };

    // 创建Form表单
    let form = $('<form></form>');
    // 确保不走缓存。
    form.attr('action', url + "&r=" + Math.random());
    form.attr('method', 'post');
    for (let item in param) {
      let input = $('<input type="text" />');
      input.attr('value', JSON.stringify(param[item]));
      input.attr('name', item);
      form.append(input);
    }
    form.appendTo('body').submit().remove();

    // 如果有回调，则触发。
    if (callback) {
      callback({
        content: param,
        status: {
          code: 200,
          message: "Success."
        }
      });
    }
  },
  showMask: function () {
    let o = this;
    if ($("body").find(".mask").length == 0 && $("body").find(".maskApp").length == 0) {
      // this.mask.append("<div class='icon icon-73dengdai'></div>")
      this.mask.append("<div class='load'></div>")
      $("body").prepend(this.mask);
    }

    this.mask.show();

    // 因为遮罩只能挡住当前可视区，所以要禁用滚动条，等加载完成后再恢复。
    $(document.body).css("overflow", "hidden");

    // 如果是APP，则模拟进度加载。
    if (this.engine.tools.isMobile()) {
      let loadContent = $(this.mask).find(".load");
      this.loadProcess = 0;
      loadContent.width(this.loadProcess);

      this.showMaskByAPPProcess(loadContent);

    }

    // 打开遮罩同时，启动遮罩监听；
    monitorMask();
    let timespan = 200; // 设置500毫秒监听，完成最后一个请求后关闭遮罩并且停止循环。
    let maxCount = 0;//10秒后不管加载是否完成，都关闭遮罩

    function monitorMask() {
      setTimeout(function () {
        // 监控好以后完成关闭。
        if (o.ajaxSequence == 0 || maxCount > 1000 * 10) {
          o.hiddenMask();
          // console.log(o.ajaxSequence);
        }
        // 每隔开1秒执行一次；
        else {
          setTimeout(function () {
            maxCount = maxCount + timespan;// 加500毫秒；
            monitorMask();
          }, timespan);
        }
      }, timespan);
    }
  },
  showMaskByAPPProcess: function (loadContent) {
    // 只有大于0，才执行。
    let o = this;
    let totalWidth = $(this.mask).width();
    // alert(totalWidth);
    // let loadContent = $(this.mask).find(".load");
    if (this.loadProcess <= totalWidth) {
      // 一半之前
      if (this.loadProcess <= totalWidth / 2) {
        this.loadProcess = this.loadProcess + 20;
      }
      // 完成三分之二了，加5个。
      else if ((this.loadProcess / totalWidth) <= 0.8) {
        this.loadProcess = this.loadProcess + 5;
      }
      // 超过加1个。
      else {
        this.loadProcess = this.loadProcess + 1;
      }

      loadContent.width(this.loadProcess);
      setTimeout(function () {
        o.showMaskByAPPProcess(loadContent);

      }, 500);
    }
  },
  hiddenMask: function () {
    // 隐藏进度条
    this.mask.hide();
    //
    $(document.body).css("overflow-y", "auto");
  }
}

export {
  oRequestEngine
}


